import { ITakeMaterial } from "@/interface/common";
import { BaseModel, IBaseModelOption } from "@/ts/BaseModel";
import { Box3Controls } from "@/utils/Box3Controls";
import { RaycasterControls } from "@/utils/RaycasterControls";
import { loadUrl, throttle } from "@/utils/Utils";
import * as THREE from "three"
import EditorHistory from "./EditorHistory";
import EditorSetPostionCommands from "./commands/EditorSetPostionCommands";
import { EnumEditorCommandType } from "./commands/EditorCommands";
import EditorSetScaleCommands from "./commands/EditorSetScalcCommands";
import EditorSetRotateCommands from "./commands/EditorSetRotateCommands";
import EditorHistoryStore from "./EditorHistoryStore";
import EditorAddObjectCommand from "./commands/EditorAddObjectCommands";
import EditorSetMaterialCommands from "./commands/EditorSetMaterialCommands";
import { ILightType } from "../components/OperationalInformation/EditorLightComponent/EditorLightComponent";
import { windowMouseTo3DMouse } from "@/utils/Mouse";
import DragObject from "@/utils/DragObject";

/** 选中模型 */
export const EVENT_SELECT_MESH = 'EVENT_SELECT_MESH';
/** 取消选中模型 */
export const EVENT_DESELECT_MESH = 'EVENT_DESELECT_MESH';
/** 更新transform控制器 */
export const EVENT_CHANGE_TRANSFORM_CONTROLS = 'EVENT_CHANGE_TRANSFORM_CONTROLS';
export const EVENT_EDITOR_CHANGE_TRANSFORM_CONTROLS = 'EVENT_EDITOR_CHANGE_TRANSFORM_CONTROLS';
export const EVENT_UPDATE_EDITOR_MODELS = 'EVENT_UPDATE_EDITOR_MODELS';

export interface IUserData {
    /** 上一次材质的贴图 */
    // oldTextureMapping?: THREE.Texture | null,
    /** 上一次的材质 */
    oldMaterial: THREE.Material | THREE.Material[],
    /** 上一个父元素的uuid */
    oldParentGroupUuid?: string,
    /** 是否是box3的mesh */
    isBox3?: boolean,
}
export interface IEditorModelsOptions extends IBaseModelOption {
}
export class EditorModels extends BaseModel {

    mouse = new THREE.Vector2(0, 0);
    /** 当前创建的模型 */
    currentCreateModel: THREE.Group | null = null;
    /** 当前拖拽的材质 */
    currentDragMaterial: THREE.Material | null = null;
    /** 当前拖拽的贴图 */
    currentDragTextureMap: THREE.Texture | null = null;
    /** 射线控制器 */
    raycasterControls !: RaycasterControls;
    /** 场景中被移上去触发的集合 */
    sceneMoveObject3DList: THREE.Group[] = [];
    /** 上一个被鼠标移上去触发的模型 */
    preMoveObject3DGroup: THREE.Group | undefined = undefined;
    /**  当前选中的模型 */
    selectMeshGroup: THREE.Group | undefined = undefined;
    /** 鼠标移上去触发的材质样式 */
    moveMaterial = new THREE.MeshBasicMaterial({
        color: 0xffff00,
    })
    /** 上一个被拖拽到上面触碰到的模型 */
    currentDragMesh: THREE.Mesh | undefined = undefined;
    /** 历史记录 */
    histroy: EditorHistory;
    /** 更新transform控制器 */
    changeTransformControls = throttle(() => {
        this.$emit(EVENT_CHANGE_TRANSFORM_CONTROLS);
    }, 300)
    /** 更新视图 */
    updataEditorModels = throttle((aa) => {
        this.updateView();
    }, 60)
    /** 变换控制器按下去的信息记录 */
    transformControlsMouseDownObject = {
        position: new THREE.Vector3(),
        rotation: new THREE.Euler(),
        scale: new THREE.Vector3(),
    }
    /** 历史存储 */
    historyStore: EditorHistoryStore
    /** 是否已经完成拖拽事件 */
    isDragComplete = false;
    /** 拖拽对象 */
    dragObject: DragObject;
    /** 初次拖拽进入到视图的时候 */
    isDragFirstInCavans = true;
    constructor(option: IEditorModelsOptions) {
        super(option)
        this.option = {
            ...this.option,
            ...option
        };
        this.loadScene();
        this.histroy = new EditorHistory({
            editor: this
        });
        this.historyStore = new EditorHistoryStore({
            editorModels: this
        })
        let { clientWidth, clientHeight } = this.renderer.domElement;
        this.raycasterControls = new RaycasterControls(this.camera, { width: clientWidth, height: clientHeight });
        this.transformControls.addEventListener('change', this.updataEditorModels);
        this.transformControls.addEventListener('mouseUp', this.transformControlsMouseUp.bind(this));
        this.transformControls.addEventListener('mouseDown', this.transformControlsMouseDown.bind(this));
        this.orbitControls.addEventListener('change', this.updataEditorModels)
        this.dragObject = new DragObject(new THREE.Group, this.camera, this.renderer.domElement)
    }

    /** 变换控制器鼠标按下事件 */
    transformControlsMouseDown() {
        const object = this.transformControls.object;
        if (!object) return;
        this.transformControlsMouseDownObject = {
            position: object.position.clone(),
            rotation: object.rotation.clone(),
            scale: object.scale.clone(),
        }
    }

    /** 变换控制器鼠标弹起事件 */
    transformControlsMouseUp() {
        const object = this.transformControls.object;
        if (!object) return;
        switch (this.transformControls.getMode()) {
            case 'translate':
                this.histroy.execute(new EditorSetPostionCommands({
                    editor: this,
                    object: object,
                    newPosition: object.position,
                    oldPosition: this.transformControlsMouseDownObject.position
                }), EnumEditorCommandType.SetPositionCommand);
                break;
            case 'rotate':
                this.histroy.execute(new EditorSetRotateCommands({
                    editor: this,
                    object: object,
                    newRotation: object.rotation,
                    oldRotation: this.transformControlsMouseDownObject.rotation
                }), EnumEditorCommandType.SetRotateCommand);
                break;
            case 'scale':
                this.histroy.execute(new EditorSetScaleCommands({
                    editor: this,
                    object: object,
                    newScale: object.scale,
                    oldScale: this.transformControlsMouseDownObject.scale
                }), EnumEditorCommandType.SetScaleCommand);
                break;

        }
    }



    /** 创建模型到场景中 */
    async creatModelInPlane(modelUrl: string) {
        this.isDragComplete = false;
        let { scene } = await this.loadGlbModel(modelUrl);
        /**如果异步获取得到模型的速度，慢于拖拽完成的速度，则后面不执行 */
        if (this.isDragComplete && this.currentCreateModel === null) return;
        let { group, box3Controls } = this.creatBox3AndToGroupCenter(scene, 'box3-scene-' + Box3Controls.numberInstance, '模型-' + Box3Controls.numberInstance);
        let modelSize = box3Controls.box3.getSize(new THREE.Vector3());
        let maxSize = Math.max(modelSize.x, modelSize.y, modelSize.z);
        let targetSize = 5;
        let scale = targetSize / maxSize;
        this.currentCreateModel = group;
        group.traverse((item: THREE.Object3D | ITakeMaterial) => {
            let userData: IUserData = item.userData as IUserData;
            if ('material' in item) {
                if (item.name.includes('box3')) {
                    userData.isBox3 = true;
                }
                userData.oldMaterial = item.material;
                // userData.oldTextureMapping = 'map' in item.material ? (item.material.map as THREE.Texture | null) : undefined;
                userData.oldParentGroupUuid = group.uuid;
            }
        })
        group.rotateY(this.orbitControls.getAzimuthalAngle())
        group.visible = false;
        group.scale.set(scale, scale, scale);
        this.scene.add(group);
    }

    /** 改变创建模型是的位置 */
    changeCreateModelInPlanePosition(mouse: THREE.Vector2) {
        if (!this.currentCreateModel) return;
        this.setMouse(mouse);
        let { isRayToMesh, firstData } = this.raycasterControls.getMouseRayToMesh([this.ground]);
        if (!isRayToMesh) return;
        let rules = Box3Controls.getRuleBox3ByMesh(this.currentCreateModel);
        this.currentCreateModel.children[1].visible = true; // 包围盒设置成可见
        this.currentCreateModel.position.set(firstData.point.x, (rules.height / 2), firstData.point.z); // 设置物体到平面上
        this.currentCreateModel.visible = true;
    }

    /** 完成模型创建到场景中 */
    completeCreateModelInPlane() {
        this.isDragComplete = true;
        this.completeCreateMeshCommon();
    }

    completeCreateMeshCommon() {
        if (!this.currentCreateModel || !this.currentCreateModel.visible) return;
        this.currentCreateModel.children[1].visible = false;
        this.sceneMoveObject3DList.push(this.currentCreateModel);
        this.histroy.execute(new EditorAddObjectCommand({
            editor: this,
            object: this.currentCreateModel,
        }), EnumEditorCommandType.AddObjectCommand);
        this.currentCreateModel = null;
    }

    /** 创建灯光到场景中 */
    creatLightInPlane(lightType: ILightType) {
        let light: THREE.Group = new THREE.Group();
        switch (lightType) {
            case 'PointLight':
                light = this.loadPointLightGroup();
                break
            case 'SpotLight':
                light = this.loadSpotLightGroup();
                break
        }
        let { group } = this.creatBox3AndToGroupCenter(light, 'box3-scene-' + Box3Controls.numberInstance, lightType + '光-' + Box3Controls.numberInstance);
        group.traverse((item: THREE.Object3D | ITakeMaterial) => {
            let userData: IUserData = item.userData as IUserData;
            if ('material' in item) {
                if (item.name.includes('box3')) {
                    userData.isBox3 = true;
                }
                userData.oldMaterial = item.material;
                userData.oldParentGroupUuid = group.uuid;
            }
        })
        this.currentCreateModel = group;
        this.dragObject.setSelelcted(group);
        this.scene.add(group)
        group.visible = false;

    }
    /** 改变灯光的位置 */
    changecreatLightInPlanePosition(mouse: THREE.Vector2) {
        if (!this.currentCreateModel) return;
        this.setMouse(mouse);
        if (this.isDragFirstInCavans) {
            this.isDragFirstInCavans = false
            this.dragObject.onPointerDown(mouse)
        }
        this.dragObject.onPointerMove(mouse)
        this.currentCreateModel.children[1].visible = true; // 包围盒设置成可见
        this.currentCreateModel.visible = true;
    }


    /** 完成灯光创建到场景中 */
    completeCreateLightInPlane() {
        this.completeCreateMeshCommon();
    }



    /** 设置当前拖拽的材质 */
    setCurrentDragMaterial(material: THREE.Material) {
        this.currentDragMaterial = material;
    }

    /** 拖拽材质到物体上 */
    dragChangeMeshMaterial(mouse: THREE.Vector2) {
        if (!this.currentDragMaterial) return;
        this.setMouse(mouse);
        //过滤掉平面
        let { isRayToMesh, firstMesh } = this.raycasterControls.getMouseRayToMesh(this.sceneMoveObject3DList, (list) => {
            return list.filter(item => !((item.object.userData as IUserData).isBox3))
        });
        // 重置上次的材质
        if (this.currentDragMesh) {
            this.currentDragMesh.material = (this.currentDragMesh.userData as IUserData).oldMaterial
        }
        if (!isRayToMesh || !(firstMesh instanceof THREE.Mesh)) {
            this.currentDragMesh = undefined;
            return
        }
        firstMesh.material = this.currentDragMaterial;
        this.currentDragMesh = firstMesh
    }

    /** 完成将材质球拖拽到物体上 */
    dragCompleteMeshMaterial() {
        if (!this.currentDragMesh || !this.currentDragMaterial) return;
        this.histroy.execute(new EditorSetMaterialCommands({
            editor: this,
            object: this.currentDragMesh,
            newMaterial: this.currentDragMaterial,
            oldMaterial: (this.currentDragMesh.userData as IUserData).oldMaterial
        }), EnumEditorCommandType.SetMaterialCommand)
        this.currentDragMesh.material = this.currentDragMaterial!;
        (this.currentDragMesh.userData as IUserData).oldMaterial = this.currentDragMaterial!;
    }

    /** 设置当前拖拽的贴图 */
    async setCurrentDragTextureMap(textureMapImage: string) {
        let texture = await this.loadTextureMap(textureMapImage);
        this.currentDragTextureMap = texture;
    }

    /**
     * 拖拽贴图到物体上
     * @param mouse 鼠标位置
     * @param mapType 什么类型的贴图
     * @returns 
     */
    dragChangeMeshTextureMap(mouse: THREE.Vector2, mapType = "map") {
        if (!this.currentDragTextureMap) return;
        this.setMouse(mouse);
        //过滤掉平面
        let { isRayToMesh, firstMesh } = this.raycasterControls.getMouseRayToMesh(this.sceneMoveObject3DList, (list) => {
            return list.filter(item => !((item.object.userData as IUserData).isBox3))
        });
        if (this.currentDragMesh) {
            this.currentDragMesh.material = (this.currentDragMesh.userData as IUserData).oldMaterial
        }
        if (!isRayToMesh || !(firstMesh instanceof THREE.Mesh)) {
            this.currentDragMesh = undefined;
            return
        }
        let newMaterial = firstMesh.material.clone();
        newMaterial[mapType] = this.currentDragTextureMap;
        newMaterial.needsUpdate = true;
        firstMesh.material = newMaterial;
        this.currentDragMaterial = newMaterial;
        this.currentDragMesh = firstMesh
    }

    /** 完成将贴图拖拽到物体上 */
    dragCompleteMeshTextureMap() {
        if (!this.currentDragMesh || !this.currentDragTextureMap || !this.currentDragMaterial) return;
        this.histroy.execute(new EditorSetMaterialCommands({
            editor: this,
            object: this.currentDragMesh,
            newMaterial: this.currentDragMaterial,
            oldMaterial: (this.currentDragMesh.userData as IUserData).oldMaterial
        }), EnumEditorCommandType.SetMaterialCommand)
        this.currentDragMesh.material = this.currentDragMaterial!;
        (this.currentDragMesh.userData as IUserData).oldMaterial = this.currentDragMaterial!;
    }

    /** 加载初始化场景 */
    async loadScene() {
        this.init();
        this.animationLoop();
    }


    /** 当鼠标移到模型上时，使其改变颜色（表示可选状态等） */
    mouseOnModel() {
        let { firstMesh, isRayToMesh } = this.raycasterControls.getMouseRayToMesh(this.sceneMoveObject3DList, (list) => {
            return list.filter(item => !((item.object.userData as IUserData).isBox3))
        });
        if (!isRayToMesh && !this.preMoveObject3DGroup) return;
        // 触发离开时
        if (!isRayToMesh && this.preMoveObject3DGroup) {
            BaseModel.changeGroupMaterial(this.preMoveObject3DGroup, (item) => (item.userData as IUserData).oldMaterial)
            this.preMoveObject3DGroup = undefined;
            return;
        }
        let userData: IUserData = firstMesh.userData as IUserData;
        // 找到选中的group并添加选中样式
        let findMeshGroup = this.sceneMoveObject3DList.find(item => item.uuid === userData.oldParentGroupUuid);
        if (findMeshGroup) {
            // 如果已经选中的模型和现在找到的模型相同，则不触发样式修改
            if (this.selectMeshGroup?.uuid === findMeshGroup.uuid) return;
            // 如果这次的uuid和上次的uuid相同时，则仍未还是目标元素，直接return;
            if (findMeshGroup?.uuid === this.preMoveObject3DGroup?.uuid) return;
            BaseModel.changeGroupMaterial(this.preMoveObject3DGroup!, (item) => (item.userData as IUserData).oldMaterial!);
            this.preMoveObject3DGroup = findMeshGroup;
            BaseModel.changeGroupMaterial(findMeshGroup, (item) => {
                if (!(item.userData as IUserData).isBox3) {
                    return this.moveMaterial
                }
            });
        }
    }

    /** 鼠标点击到模型 */
    clickOnModel() {
        let rayMeshList = this.selectMeshGroup ? [...this.sceneMoveObject3DList, ...this.transformControlsMeshList] : this.sceneMoveObject3DList;
        let { firstMesh, isRayToMesh } = this.raycasterControls.getMouseRayToMesh(rayMeshList.filter(item => item.visible), (list) => {
            // if (this.selectMeshGroup) return list.filter(item => item.object.visible)
            // return list.filter(item => !((item.object.userData as IUserData).isBox3))
            return list.filter(item => item.object.visible)
        });
        if (!isRayToMesh) {
            this.transformControls.detach();
            if (this.selectMeshGroup) {
                this.selectMeshGroup.children[1].visible = false;
            }
            this.selectMeshGroup = undefined;
            return;
        }
        let userData: IUserData = firstMesh.userData as IUserData;
        // 找到选中的group并添加选中样式
        let findMeshGroup = this.sceneMoveObject3DList.find(item => item.uuid === userData.oldParentGroupUuid);
        if (findMeshGroup) {
            this.selectMeshGroupFunction(findMeshGroup)
        }

    }

    /** 鼠标选中的集合元素 */
    selectMeshGroupFunction(findMeshGroup: THREE.Group) {
        if (findMeshGroup === this.selectMeshGroup) return;
        if (findMeshGroup) {
            findMeshGroup.children[1].visible = true; // 将box3盒子显示
            if (this.selectMeshGroup) {
                this.selectMeshGroup.children[1].visible = false;
            }
            this.selectMeshGroup = findMeshGroup;
            this.$emit(EVENT_SELECT_MESH, this.selectMeshGroup);
            this.transformControls.attach(findMeshGroup!)
        }
    }

    /** 取消选中 */
    deselectMeshGroupFunction() {
        if (this.selectMeshGroup) {
            this.selectMeshGroup.children[1].visible = false;
        }
        this.selectMeshGroup = undefined;
        this.$emit(EVENT_DESELECT_MESH);
        this.transformControls.detach()
    }



    /** 更新鼠标位置 */
    updateMouse(mouse: THREE.Vector2) {
        this.setMouse(mouse)
        this.mouseOnModel();
    }

    /** 鼠标点击事件 */
    clickMouse(mouse: THREE.Vector2) {
        this.setMouse(mouse)
        this.clickOnModel();
    }

    /** 改变鼠标位置 */
    setMouse(mouse: THREE.Vector2) {
        this.mouse = mouse;
        this.raycasterControls.mouse = mouse;
    }

    /** 更新视图 */
    updateView() {
        this.$emit(EVENT_UPDATE_EDITOR_MODELS);
    }

    /** 根据uuid 获取object */
    objectByUuid(uuid: string) {
        return this.scene.getObjectByProperty('uuid', uuid);
    }

    /** 持续更新 */
    animationLoop() {
        this.renderer.setAnimationLoop(() => {
            this.updata();
        });
    }


}